/*
 * This file is part of the Sx Framework Library.
 * 
 * Copyright (C) 2013 University of Colorado Denver
 * <min.choi@ucdenver.edu> <shane.transue@ucdenver.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */
#ifndef SX_PAIR_LIST_H
#define SX_PAIR_LIST_H

#include "ArrayList.h"

namespace Sx {
namespace Util {

/*
 * The PairList class provides a utility class for the use of two associated
 * arrays. This provides a useful shortcut to manipulating associated arrays
 * of differing types. This is useful in a similar context as a map but ensures
 * order.
 */
template <typename T, typename U>
class PairList {
public:
	PairList();
	PairList(unsigned int size);
	~PairList();

	void add(const T& primary, const U& secondary);
	void addFirst(const T& primary, const U& secondary);
	void addLast(const T& primary, const U& secondary);
	void push_front(const T& primary, const U& secondary);
	void push_back(const T& primary, const U& secondary);

	bool remove(unsigned int index);
	void removeFirst();
	void removeLast();
	bool pop_front();
	bool pop_back();

	void clear();
	void empty();

	void setPrimaryAt(unsigned int index, const T& data);
	void setSecondaryAt(unsigned int index, const U& data);
	void insert(unsigned int index, const T& primary, const U& secondary);

	T getPrimaryAt(unsigned int index);
	U getSecondaryAt(unsigned int index);

	T getPrimary(const U& secondary);
	U getSecondary(const T& primary);

	const T* constPrimaryData() const;
	const U* constSecondaryData() const;

	unsigned int size() const;

private:
	ArrayList<T> primary;
	ArrayList<U> secondary;
};

template <typename T, typename U>
Sx::Util::PairList<T, U>::PairList() {
	this->primary = ArrayList<T>();
	this->secondary = ArrayList<U>();
}

template <typename T, typename U>
Sx::Util::PairList<T, U>::PairList(unsigned int size) {
	this->primary = ArrayList<T>(size);
	this->secondary = ArrayList<U>(size);
}

template <typename T, typename U>
Sx::Util::PairList<T, U>::~PairList() {}

template <typename T, typename U>
void Sx::Util::PairList<T, U>::add(const T& primary, const U& secondary) {
	this->primary.add(primary);
	this->secondary.add(secondary);
}

template <typename T, typename U>
void Sx::Util::PairList<T, U>::addFirst(const T& primary, const U& secondary) {
	this->primary.addFirst(primary);
	this->secondary.addFirst(secondary);
}

template <typename T, typename U>
void Sx::Util::PairList<T, U>::addLast(const T& primary, const U& secondary) {
	this->primary.addLast(primary);
	this->secondary.addLast(secondary);
}

template <typename T, typename U>
void Sx::Util::PairList<T, U>::push_front(const T& primary, const U& secondary) {
	this->primary.push_front(primary);
	this->secondary.push_front(primary);
}

template <typename T, typename U>
void Sx::Util::PairList<T, U>::push_back(const T& primary, const U& secondary) {
	this->primary.push_back(primary);
	this->seoncdary.push_back(secondary);
}

template <typename T, typename U>
bool Sx::Util::PairList<T, U>::remove(unsigned int index) {
	bool s1, s2;
	s1 = this->primary.removeAt(index);
	s2 = this->secondary.removeAt(index);

	if ( s1 && s2 ) return true;
	else return false;
}

template <typename T, typename U>
void Sx::Util::PairList<T, U>::removeFirst() {
	this->primary.removeFirst();
	this->primary.removeLast();
}

template <typename T, typename U>
void Sx::Util::PairList<T, U>::removeLast() {
	this->primary.removeLast();
	this->secondary.removeLast();
}

template <typename T, typename U>
bool Sx::Util::PairList<T, U>::pop_front() {
	this->primary.pop_front();
	this->secondary.pop_front();
}

template <typename T, typename U>
bool Sx::Util::PairList<T, U>::pop_back() {
	this->primary.pop_back();
	this->secondary.pop_back();
}

template <typename T, typename U>
void Sx::Util::PairList<T, U>::clear() {
	this->empty();
}

template <typename T, typename U>
void Sx::Util::PairList<T, U>::empty() {
	this->primary.empty();
	this->secondary.empty();
}

template <typename T, typename U>
void Sx::Util::PairList<T, U>::setPrimaryAt(unsigned int index, const T& data) {
	this->primary.set(index, data);
}

template <typename T, typename U>
void Sx::Util::PairList<T, U>::setSecondaryAt(unsigned int index, const U& data) {
	this->secondary.set(index, data);
}

template <typename T, typename U>
void Sx::Util::PairList<T, U>::insert(unsigned int index, const T& primary, const U& secondary) {
	this->primary.insert(index, primary);
	this->secondary.insert(index, secondary);
}

template <typename T, typename U>
T Sx::Util::PairList<T, U>::getPrimaryAt(unsigned int index) {
	return this->primary.get(index);
}

template <typename T, typename U>
U Sx::Util::PairList<T, U>::getSecondaryAt(unsigned int index) {
	return this->secondary.get(index);
}

template <typename T, typename U>
T Sx::Util::PairList<T, U>::getPrimary(const U& secondary) {
	return this->primary.get(this->secondary.indexOf(secondary));
}

template <typename T, typename U>
U Sx::Util::PairList<T, U>::getSecondary(const T& primary) {
	return this->secondary.get(this->primary.indexOf(primary));
}

template <typename T, typename U>
const T* Sx::Util::PairList<T, U>::constPrimaryData() const {
	return this->primary.constData();
}

template <typename T, typename U>
const U* Sx::Util::PairList<T, U>::constSecondaryData() const {
	return this->secondary.constData();
}

template <typename T, typename U>
unsigned int Sx::Util::PairList<T, U>::size() const {
	if ( this->primary.size() != this->secondary.size() ) {
		std::cerr << "[PairList:size] Error: The primary and secondary arrays do not have matching sizes." << std::endl;
		std::cerr << "  Clear this pair array and repopulate it to correct this problem." << std::endl;
		std::cerr << "  Returning the difference between the sizes of the primary and secondary arrays." << std::endl;
		return std::abs(static_cast<int>(this->primary.size()) - static_cast<int>(this->secondary.size()));
	}
	else return this->primary.size();
}

}

}

#endif
